#ifndef FS_H
#define FS_H

#include <type.h>

#define MAGIC 0x31415926
#define SUPERBLOCK_START_BLOCK 0x20000
#define BLOCKMAP_START_BLOCK 0x20001
#define INODEMAP_START_BLOCK 0x20005
#define INODE_START_BLOCK 0x20006
#define DATA_START_BLOCK 0x20100
#define SUPERBLOCK_START_SECTOR 0x100000         //start at 512MB in SD card
#define BLOCKMAP_START_SECTOR 0x100008
#define INODEMAP_START_SECTOR 0x100028
#define INODE_START_SECTOR 0x100030
#define DATA_START_SECTOR 0x100800

#define SECTORS_PER_BLOCK 8

#define TYPE_DENTRY 1
#define TYPE_FILE 2

/* mode for files */
#define RDONLY 1  /* read only */
#define WRONLY 2  /* write only */
#define RDWR 3    /* read/write */

/* block nums for index type */
#define DIRECT_INDEX_MAX 8
#define FIRST_INDEX_MAX 0x400
#define SECOND_INDEX_MAX 0x100000

typedef struct superblock {
    uint32_t magic;
    uint32_t fs_block_num;              //block size is 4KB
    uint32_t start_block_id;

    uint32_t inodes_cnt;
    uint32_t data_blocks_cnt;
    uint32_t inodes_used_cnt;
    uint32_t blocks_used_cnt;

    uint32_t blockmap_block_offset;
    uint32_t inodemap_block_offset;
    uint32_t inode_block_offset;
    uint32_t data_block_offset;

    uint32_t inode_size;                //count in bytes
    uint32_t dentry_size;

    uint32_t root_inode_num;            // info of root dentry
    uint32_t root_blockid;

    uint32_t padding[113];              //1 superblock occupy 1 sector = 512 bytes
}superblock_t;


typedef struct inode {
    uint32_t inode_num;
    uint32_t type;                      //0:unused 1:dentry 2:file
    uint32_t link;                      //inode num of which this inode linked to(soft); 0 for unused
    uint32_t ref_num;                   //for dentry
    uint32_t used_size;                 //unit: bytes
    uint32_t used_blocks;
    uint32_t direct[8];
    uint32_t first;
    uint32_t second;
}inode_t;                               //1 inode occupy 64 bytes


typedef struct dentry_content {
    uint8_t type;                       //0: unused, 1: dentry, 2: file
    char name[27];
    uint32_t inode_idx;
}d_content_t;                           //1 elem occupy 32 bytes
typedef struct dentry {
    d_content_t content[128];
}d_entry_t;                             //1 dentry occupy 4KB = 1 block

typedef struct fd {
    uint32_t inode_num;
    uint16_t used;
    uint16_t availability;
    uint32_t read_pos;
    uint32_t write_pos;
}fd_t;                                  //1 fd occupy 16 bytes

extern superblock_t *spb;
extern d_entry_t *cur_dentry;
extern inode_t *cur_dentry_inode;

extern int  do_mkfs();
extern void do_statfs();
extern int  do_mkdir(char *name);
extern int  do_rmdir(char *name);
extern void do_show_files();
extern int  do_cd(char *way);

extern int  do_mkfile(char *name, uint32_t block_size);
extern void do_cat(char *name);
extern int  do_openfile(char *name, uint32_t access);
extern int  do_readfile(uint32_t fd, char *buff, uint32_t size);
extern int  do_writefile(uint32_t fd, char *buff, uint32_t size);
extern void do_closefile(uint32_t fd);
extern void do_ln(char *src, char *dest);
extern void do_ln_s(char *src, char *dest);
extern void do_movepos(uint32_t fd, uint32_t type, uint32_t pos);

void allocMemForFsInKer();
int  allocInode();
void freeInode(uint32_t inode_num);
void read_inode(uint32_t inode_num, inode_t *inode);
void write_inode(uint32_t inode_num, inode_t *inode);
int  find_inode(char *way);
int  allocBlock();
void freeBlock(uint32_t block_id);
void delete_dentry(uint32_t inode_num);
void make_root();
void make_index(uint32_t block_size, inode_t* inode);
int  search_index(uint32_t pos, inode_t *inode);
int  allocFd();

#endif